import * as express from "express";
import * as path from "path";
import { Server } from "ws";
// import { cpus } from "os";

const app = express(); //声明服务器端的http服务。

// 用post请求一定要加载的方法

var bodyParser = require("body-parser");  
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));  

export class Product {
    constructor(
      public id: number,
      public title: string,
      public price: number,
      public rating: number,
      public desc: string,
      public imgUrl: string,
      public categolary: Array<string>
    ) {}
  };
  

const products:Product[] = [
    new Product(1, '鞋子', 280, 3, '商品描述一', '260x150', [ '服饰', '流行单品' ]),
    new Product(2, '帽子', 20, 2.1, '商品描述二', '260x150', [ '服饰', '配饰' ]),
    new Product(3, '电脑', 2000, 3.5, '商品描述三', '260x150', [ '数码产品', '硬件' ]),
    new Product(4, '保温杯', 160, 4.5, '商品描述四', '260x150', [ '生活用品']),
    new Product(5, '衣柜', 15220, 3, '商品描述五', '260x150', [ '家具', '套件' ]),
    new Product(6, 'IQ84', 13, 5, '商品描述六', '260x150', [ '书籍', '畅销书' ])
  ];

  export class Commit {

    constructor(
      public id: number,
      public content: string,
      public productId: number,
      public username: string,
      public date: string,
      public rating: number
    ){}
  }

  const commits: Commit[] = [
    new Commit(1, '很好', 2, '李白', '2019-02-21',3),
    new Commit(2, '挺好', 1, '杜甫', '2018-02-21',4),
    new Commit(3, '蛮好', 4, '孟浩然', '2016-02-21',3.2),
    new Commit(4, '差', 5, '陶渊明', '2017-02-21',5),
    new Commit(5, '很差', 6, '李绅', '2019-04-16',2.5),
    new Commit(6, '一般', 2, '杜甫', '2017-05-21',4.5),
    new Commit(7, '最好', 1, '诗仙', '2015-06-15',3.4),
    new Commit(8, '挺好', 3, '李商隐', '2017-05-26',1.5),
    new Commit(9, '一般', 4, '元稹', '2018-12-24',2.5),
    new Commit(10, '很好', 5, '胡雪岩', '2018-05-26',3.5),
    new Commit(11, '差', 6, '王之涣', '2018-06-26',4.5),
    new Commit(12, '退货', 2, '辛弃疾', '2018-05-29',1.5),
    new Commit(13, '很好', 2, '李白', '2019-02-21',3),
    new Commit(14, '挺好', 1, '杜甫', '2018-02-21',4),
    new Commit(15, '蛮好', 4, '孟浩然', '2016-02-21',3.2),
    new Commit(16, '差', 5, '陶渊明', '2017-02-21',5),
    new Commit(17, '很差', 6, '李绅', '2019-04-16',2.5),
    new Commit(18, '一般', 2, '杜甫', '2017-05-21',4.5),
    new Commit(19, '最好', 1, '诗仙', '2015-06-15',3.4),
    new Commit(20, '挺好', 3, '李商隐', '2017-05-26',1.5),
    new Commit(21, '一般', 4, '元稹', '2018-12-24',2.5),
    new Commit(22, '很好', 5, '胡雪岩', '2018-05-26',3.5),
    new Commit(23, '差', 6, '王之涣', '2018-06-26',4.5),
    new Commit(24, '退货', 2, '辛弃疾', '2018-05-29',1.5),
  ]  

 
  function computRating(products:Product[], commits:Commit[]){
    let sum:number = 0;
    let k:number = 0;
    for(let i=0; i< products.length; i++){
      sum = 0;
      k = 0;
      for(let j=0; j< commits.length; j++){
          if(products[i].id == commits[j].productId){
            sum += Number(commits[j].rating);
            k++;
          }
      }
      products[i].rating = sum/k;
    }
  }

  computRating(products,commits);


// 将主页面设置为客户端页面，express.static加载静态资源
// app.use('/', express.static(path.join(当前目录, 上一级, 'client文件夹')))
app.use('/', express.static(path.join(__dirname, '..', 'client')))



//利用express框架处理http请求，http请求不能搭配定时器，会报错504,res.json发给客户端的是一个流对象.
  
// app.get('/',(req,res)=>{
//     res.send("Hello express");
// })




app.get('/api/products', function (req, res) {
   
    // res.send("商品查询!!!");
    res.json(products);
})

app.get('/api/product', function (req, res) {

    let results = products;
    let params = req.query;
    if(params.title){
       //***一定注意，有花括号的时候一定要写return
        results = results.filter((result: any) => { return result.title.indexOf(params.title) != -1});
    }
    if(params.price && results.length > 0){
       results = results.filter((product) => { return product.price <= params.price});
    }
    if(params.category != -1 && results.length > 0){
        results = results.filter((product) => { return product.categolary.indexOf(params.category) !== -1});
    }
    res.json(results);
})

app.get('/api/products/:id', (req, res) => {
    // res.send("商品查询!!!");
    // res.json(products);

    res.json(products.find((product) => Number(product.id) === Number(req.params.id)));

//注意，两个id数据类型不同
})

app.get('/api/commits/:id', (req, res) => {
    res.json(commits.filter((commit) => Number(commit.productId) === Number(req.params.id)));
//注意，两个id数据类型不同
})

app.get('/api/commits', (req, res) => {
    res.json(commits);
})

// get添加数据
// app.get('/api/addcommits/:productId/:newCommit/:newRating/:username', (req, res) => {
//     commits.unshift(new Commit(commits.length+1, req.params["newCommit"], req.params["productId"],req.params["username"], new Date().toISOString(), req.params["newRating"]));
//     computRating(products,commits);
//     res.json(commits);
// })

// post方法添加数据
app.post('/api/addcommits',(req,res) => {
    commits.unshift(new Commit(commits.length+1,req.body.content, req.body.productId, req.body.username, new Date().toISOString(), req.body.rating));
    computRating(products, commits);
    res.json(commits);
});

const server = app.listen(8000, "localhost", () => {
    console.log("服务器启动，地址是http://localhost:8000");
})

// 创建一个websoket服务，以便进行定时推送,监听8085端口
const wsServer = new Server({port:8085});

wsServer.on("connection", websoket => { 
    // 当链接建立，给客户端推送一个消息 
     websoket.send('主动推送websocket信息');
    // 当接收到客户端发送的消息，对客户端发送的信息进行处理
     websoket.on('message', message => {
         console.log('接收到客户端消息！' + message);
     })
})
//定时推送
setInterval(()=>{
    if(wsServer.clients){
        // 给每一个链接到自己的客户端都定时推送一个消息
        wsServer.clients.forEach((client)=>{
            client.send("这是来自服务器每隔2秒主动推送的消息");
        })
    }
},2000);





// 再创建一个websoket服务，以便进行定时推送,监听8089端口

const wsProductPrice = new Server({port:8089});
// es6语法，查看Map类型（集合类型）。
let subscription = new Map<any,number[]>();
wsProductPrice.on("connection", websoket => { 
    // 当链接建立，给客户端推送一个消息
    // websoket.send(ProductCurentPrice);
   
    // 当接收到客户端发送的消息，对客户端发送的信息进行处理
     websoket.on('message', (message:any) => {
            //注意，传过来的id被解析成了字符串
            let messageObj = JSON.parse(message); 

            // console.log(typeof messageObj.productId);

            // subscription.get('key');获取productIds key值为websocket的值，没有则返回空
            // 不通过点击，只通过发送消息传过来的id则不会生成新的websoket,因此保险起见这里获取一个id数组
            let productIds = subscription.get(websoket) || [];
            
            // ...reset剩余参数，将productIds打散当参数放入值数组中，再把新传入的id放入值数组中
            subscription.set(websoket,[...productIds, Number(messageObj.productId)]);
     });
})

let currentBids = new Map<number,number>();
let currentBid:number;
// let newBid:number;
setInterval(()=>{
    // currentBids在服务器端会不断的自动读取
    products.forEach((product)=>{
        // 当前价格
        currentBid = currentBids.get(product.id) || product.price;
        // 新加价格
        let newBid = currentBid + Math.random()*5; 

        // 把每一个产品的价格都变成新价格
        currentBids.set(product.id, newBid);
     
    });
    // Map.forEach((value , key) =>{ 遍历Map类型的值与键
    subscription.forEach((productIds, ws) =>{ 
        // 当服务器与客户端连接上时，将一个带有id和对应价格的对象数组发送给客户端
        if(ws.readyState === 1){
            // .map() 遍历出值数组的每一个值
            let newBids = productIds.map( pid => {
                // console.log(typeof pid);
                return {
                    productId: pid,
                    bid: currentBids.get(pid)
                }
            });
            ws.send(JSON.stringify(newBids));
        }else{
            subscription.delete(ws);
        }
    
    });

},2000);
